////////////////////////////////////////////////////////////////////////////
//
//  Crytek Engine Source File.
//  Copyright (C), Crytek Studios, 2005.
// -------------------------------------------------------------------------
//  File name:   TimeOfDayDialog.cpp
//  Version:     v1.00
//  Created:     12/3/2002 by Timur.
//  Compilers:   Visual C++ 7.0
//  Description: 
// -------------------------------------------------------------------------
//  History:
//
////////////////////////////////////////////////////////////////////////////

#include "StdAfx.h"
#include "TimeOfDayDialog.h"
#include "Controls/SplineCtrl.h"
#include "Controls/ColorGradientCtrl.h"
#include "Controls/PropertyItem.h"
#include "Controls/CurveEditorCtrl.h"
#include "NumberDlg.h"
#include "Mission.h"
#include "CryEditDoc.h"
#include "IViewPane.h"

#include <ITimer.h>
#include <I3DEngine.h>

#include "Util\3DConnexionDriver.h"
#include "PluginManager.h"

#include "Console/ConsoleSync.h"


namespace {

enum
{
	IDC_TASKPANEL = 100,
	IDC_TIME_OF_DAY,
	IDC_TIME_OF_DAY_START,
	IDC_TIME_OF_DAY_END,
	IDC_TIME_OF_DAY_SPEED,
	IDC_SKYUPDATE_CHECKBOX,
	ID_TOGGLE_ADVANCED_PROPERTIES,
	IDC_PROPERTIES_CTRL,
	IDC_SPLINE_CTRL,
	IDC_SPLINE_CTRL_TIMELINE,
	IDC_COLOR_CTRL,
	IDC_SPLINE_TOOLBAR,
	IDC_TIMELINE_SLIDER,
	IDC_HDR_PANE,
	IDC_HDR_PROPERTIES_CTRL,

	ID_EXPAND_ALL_SPLINES,
	ID_COLLAPSE_ALL_SPLINES,
	ID_PLAY_TOD_ANIM,
	ID_STOP_TOD_ANIM,
	ID_RESET_TO_DEFAULT,
};

const int kSliderScale = 100;
const float kEpsilon = 0.00001f;

enum
{
	IDW_TASKS_PANE = AFX_IDW_CONTROLBAR_FIRST+13,
	IDW_PROPS_PANE,
	IDW_TIMELINE_PANE,
	IDW_HDR_PANE,
};

const int kTimeOfDayDialogLayoutVersion = 0x0001; // bump this up on every substantial pane layout change
#define PANE_LAYOUT_SECTION _T("DockingPaneLayouts\\TimeOfDay")
#define PANE_LAYOUT_VERSION_ENTRY _T("PaneLayoutVersion")

static void SetKeyTangentType(ISplineInterpolator* pSpline, int key, ESplineKeyTangentType type)
{
	int flags = (pSpline->GetKeyFlags(key) & ~SPLINE_KEY_TANGENT_IN_MASK) & ~SPLINE_KEY_TANGENT_OUT_MASK;
	pSpline->SetKeyFlags(key, flags | (type << SPLINE_KEY_TANGENT_IN_SHIFT) | (type << SPLINE_KEY_TANGENT_OUT_SHIFT));
}

}

//////////////////////////////////////////////////////////////////////////

class CHDRPane : public CWnd
{
	DECLARE_MESSAGE_MAP()

public:
	CHDRPane(CTimeOfDayDialog *pTODDlg, UINT nID);
	static const char* ClassName() { return "HDRPane"; }

	DECLARE_DYNAMIC(CHDRPane)

	CTimeOfDayDialog *m_pTODDlg;
	CCurveEditorCtrl m_filmCurveCtrl; 
	CPropertyCtrl m_propsCtrl;
	CVarBlockPtr m_pVars;

	void UpdateFilmCurve();

protected:
	bool Init();
	static bool RegisterWindowClass();

	bool GetFilmCurveParams(float& shoulderScale, float& midScale, float& toeScale, float& whitePoint) const;

	afx_msg void OnSize(UINT nType, int cx, int cy);
	afx_msg void OnPropertySelected(NMHDR* pNMHDR, LRESULT* pResult);
};

IMPLEMENT_DYNAMIC(CHDRPane, CWnd)

BEGIN_MESSAGE_MAP(CHDRPane, CWnd)
	ON_NOTIFY(PROPERTYCTRL_ONSELECT, IDC_HDR_PROPERTIES_CTRL, OnPropertySelected)
	ON_WM_SIZE()
END_MESSAGE_MAP()

CHDRPane::CHDRPane(CTimeOfDayDialog *pTODDlg, UINT nID)
	: m_pTODDlg(pTODDlg)
{
	assert(m_pTODDlg);
	VERIFY(RegisterWindowClass());

	const int style = WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
	if (!Create( ClassName(), "", style, CRect(0, 0, 0, 0), AfxGetMainWnd(), nID ))
	{
		assert(0);
		return;
	}

	VERIFY(Init());
}

bool CHDRPane::RegisterWindowClass()
{
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetInstanceHandle();
	if (!::GetClassInfo(hInst, ClassName(), &wndcls))
	{
		// otherwise we need to register a new class
		wndcls.style            = 0 /*CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW*/;
		wndcls.lpfnWndProc      = ::DefWindowProc;
		wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
		wndcls.hInstance        = hInst;
		wndcls.hIcon            = NULL;
		wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
		wndcls.hbrBackground    = (HBRUSH)(COLOR_3DFACE + 1);
		wndcls.lpszMenuName     = NULL;
		wndcls.lpszClassName    = ClassName();
		if (!AfxRegisterClass(&wndcls))
			return false;
	}
	return true;
}

bool CHDRPane::Init()
{
	VERIFY(m_filmCurveCtrl.Create("", WS_CHILD|WS_VISIBLE|SS_NOTIFY, CRect(0,0,200,200), this));
	m_filmCurveCtrl.SetControlPointCount(21);
	m_filmCurveCtrl.SetMouseEnable(false);
	m_filmCurveCtrl.SetPadding(0);
	m_filmCurveCtrl.SetGrid(10, 10);
	m_filmCurveCtrl.SetFlags(
		CCurveEditorCtrl::eFlag_ShowVerticalRuler |
		CCurveEditorCtrl::eFlag_ShowHorizontalRuler |
		CCurveEditorCtrl::eFlag_ShowCursorAlways |
		CCurveEditorCtrl::eFlag_ShowPaddingBorder);

	m_propsCtrl.Create(WS_CHILD|WS_VISIBLE, CRect(0,0,100,100), this, IDC_HDR_PROPERTIES_CTRL);
	m_propsCtrl.ModifyStyleEx(0, WS_EX_CLIENTEDGE);
	m_propsCtrl.SetUpdateCallback(functor(*m_pTODDlg, &CTimeOfDayDialog::OnUpdateProperties));

	return true;
}

void CHDRPane::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType,cx,cy);

	if (!::IsWindow(m_filmCurveCtrl.GetSafeHwnd())
		|| !::IsWindow(m_propsCtrl.GetSafeHwnd()))
		return;

	CRect rect;
	GetClientRect(&rect);

	int middle = rect.top + rect.Height()/2;
	m_filmCurveCtrl.MoveWindow(CRect(rect.left, rect.top, rect.right, middle));
	m_propsCtrl.MoveWindow(CRect(rect.left, middle+1, rect.right, rect.bottom));
	m_filmCurveCtrl.RedrawWindow();
}

void CHDRPane::OnPropertySelected(NMHDR* pNMHDR, LRESULT* pResult)
{
	IVariable *pVar = nullptr;
	CPropertyItem *pSelected = m_propsCtrl.GetSelectedItem();

	if (pSelected)
	{
		m_pTODDlg->m_pPropsCtrl->ClearSelection();
		pVar = pSelected->GetVariable();
	}

	if (pVar && pVar->GetType() == IVariable::ARRAY)
		pVar = nullptr;

	m_pTODDlg->ResetSpline(pVar);
}

bool CHDRPane::GetFilmCurveParams(float& shoulderScale, float& midScale, float& toeScale, float& whitePoint) const
{
	int checked = 0;
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();

	for(int i=0; pTimeOfDay->GetVariableCount(); ++i)
	{
		ITimeOfDay::SVariableInfo varInfo;
		bool ok = pTimeOfDay->GetVariableInfo(i, varInfo);
		if(ok == false)
			continue;

		switch(varInfo.nParamId)
		{
		case ITimeOfDay::PARAM_HDR_FILMCURVE_SHOULDER_SCALE:
			shoulderScale = varInfo.fValue[0];
			++checked;
			break;
		case ITimeOfDay::PARAM_HDR_FILMCURVE_LINEAR_SCALE:
			midScale = varInfo.fValue[0];
			++checked;
			break;
		case ITimeOfDay::PARAM_HDR_FILMCURVE_TOE_SCALE:
			toeScale = varInfo.fValue[0];
			++checked;
			break;
		case ITimeOfDay::PARAM_HDR_FILMCURVE_WHITEPOINT:
			whitePoint = varInfo.fValue[0];
			++checked;
			break;
		}

		if(checked == 4)
			return true;
	}

	return false;
}

static float EvalFilmCurve(float x, float ss, float ms, float ts)
{
	return (x * (ss * 6.2f * x + 0.5f * ms)) / max<float>((x * (ss * 6.2f * x + 1.7f) + ts * 0.06f), kEpsilon);
}

void CHDRPane::UpdateFilmCurve()
{
	float shoulderScale=0, midScale=0, toeScale=0, whitePoint=0;
	bool ok = GetFilmCurveParams(shoulderScale, midScale, toeScale, whitePoint); 
	assert(ok);
	const float minX=-5.0f, minY=0.0f, maxX=5.0f, maxY=1.0f;
	m_filmCurveCtrl.SetDomainBounds(minX, minY, maxX, maxY);

	int numSamplePoints = m_filmCurveCtrl.GetControlPointCount();

	for(int i = 0; i < numSamplePoints; ++i)
	{
		float t = static_cast<float>(i) / (numSamplePoints-1);
		float logX = minX + ((maxX - minX) * t);
		float x = powf(10.0f, logX); // Conventionally, the x domain is logarithmic.

		float v = EvalFilmCurve(x, shoulderScale, midScale, toeScale) / 
			max<float>(EvalFilmCurve(whitePoint, shoulderScale, midScale, toeScale), kEpsilon);
		v = powf(v, 2.2f) / 1.1f; // converting to a linear space
		                          // Dividing by 1.1 is an adhoc measure to make the shoulder visible in usual cases.

		float y = minY + ((maxY - minY) * v);

		m_filmCurveCtrl.SetControlPoint(i, Vec2(logX, y));
	}

	m_filmCurveCtrl.RedrawWindow();	
}
//////////////////////////////////////////////////////////////////////////

IMPLEMENT_DYNCREATE(CTimeOfDayDialog, CXTPFrameWnd)

//////////////////////////////////////////////////////////////////////////
class CTimeOfDayViewClass : public TRefCountBase<IViewPaneClass>
{
	//////////////////////////////////////////////////////////////////////////
	// IClassDesc
	//////////////////////////////////////////////////////////////////////////
	virtual ESystemClassID SystemClassID() { return ESYSTEM_CLASS_VIEWPANE; };
	virtual REFGUID ClassID()
	{
		// {85FB1272-D858-4ca5-ABB4-04D484ABF51E}
		static const GUID guid = { 0x85fb1272, 0xd858, 0x4ca5, { 0xab, 0xb4, 0x4, 0xd4, 0x84, 0xab, 0xf5, 0x1e } };
		return guid;
	}
	virtual const char* ClassName() { return "Time Of Day"; };
	virtual const char* Category() { return "Terrain"; };
	//////////////////////////////////////////////////////////////////////////
	virtual CRuntimeClass* GetRuntimeClass() { return RUNTIME_CLASS(CTimeOfDayDialog); };
	virtual const char* GetPaneTitle() { return _T("Time Of Day"); };
	virtual EDockingDirection GetDockingDirection() { return DOCK_FLOAT; };
	virtual CRect GetPaneRect() { return CRect(100,100,1000,800); };
	virtual CSize GetMinSize() { return CSize(400,300); }
	virtual bool SinglePane() { return false; };
	virtual bool WantIdleUpdate() { return true; };
};

CTimeOfDayDialog* CTimeOfDayDialog::m_pInstance = 0;

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::RegisterViewClass()
{
	GetIEditor()->GetClassFactory()->RegisterClass( new CTimeOfDayViewClass );
}

//////////////////////////////////////////////////////////////////////////
class CSplineCtrlContainerTOD : public CSplineCtrlEx
{
public:
	virtual void PostNcDestroy() { delete this; };

protected:
	void SelectRectangle( CRect rc,bool bSelect )
	{
		ConditionalStoreUndo();

		Vec2 vec0 = ClientToWorld( CPoint(rc.left,rc.top) );
		Vec2 vec1 = ClientToWorld( CPoint(rc.right,rc.bottom) );
		float t0 = vec0.x;
		float t1 = vec1.x;
		float v0 = vec0.y;
		float v1 = vec1.y;
		if (v0 > v1)
			std::swap(v0,v1);
		if (t0 > t1)
			std::swap(t0,t1);

		bool bFirstSelect = false;

		for (int splineIndex = 0, splineCount = m_splines.size(); splineIndex < splineCount; ++splineIndex)
		{
			ISplineInterpolator* pSpline = m_splines[splineIndex].pSpline;
			ISplineInterpolator* pDetailSpline = m_splines[splineIndex].pDetailSpline;

			for (int i = 0; i < (int)pSpline->GetKeyCount(); i++)
			{
				float t = pSpline->GetKeyTime(i);
				ISplineInterpolator::ValueType	afValue;
				pSpline->GetKeyValue(i,afValue);

				int nTotalNumberOfDimensions(pSpline->GetNumDimensions());

				ISplineInterpolator::ValueType afDetailValue;
				if (pDetailSpline)
				{
					ISplineInterpolator::ZeroValue(afDetailValue);
					pDetailSpline->Interpolate(t, afDetailValue);
				}

				for (int nCurrentDimension=0;nCurrentDimension<nTotalNumberOfDimensions;nCurrentDimension++)
				{
					if (pDetailSpline)
					{
						afValue[nCurrentDimension] = afValue[nCurrentDimension] + afDetailValue[nCurrentDimension];
					}
					if (t >= t0 && t <= t1 && afValue[nCurrentDimension] >= v0 && afValue[nCurrentDimension] <= v1)
					{
						pSpline->SelectKeyAtDimension(i, nCurrentDimension, bSelect);

						if( bFirstSelect == false )
						{
							bFirstSelect = true;
							SetTimeMarker(t);
							SendNotifyEvent(SPLN_TIME_CHANGE);
						}
					}
				}
			}
		}
		SendNotifyEvent( SPLN_CHANGE );		
	}
};

//////////////////////////////////////////////////////////////////////////

class CEnvironmentTimelineSlider : public CWnd
{
	DECLARE_MESSAGE_MAP()
public:
	CEnvironmentTimelineSlider(CTimeOfDayDialog* pEditor, int nID);
	static const char* ClassName() { return "EnvironmentTimelineCtrl"; }

	void SetTime(float fHour)
	{
		int p = fHour*kSliderScale;
		if (!m_bIgnoreScroll)
		{
			m_bIgnoreScroll = true;
			m_weightSlider.SetPos( p );
			m_weightSlider.Invalidate(TRUE);
			m_bIgnoreScroll = false;
		}
		if (p < 0)
			m_weightSlider.SetSelection(p,0);
		else if (p > 0)
			m_weightSlider.SetSelection(0,p);

	}

	DECLARE_DYNAMIC(CEnvironmentTimelineSlider)
protected:
	bool Init();
	static bool RegisterWindowClass();

	afx_msg void OnHScroll( UINT nSBCode,UINT nPos,CScrollBar* pScrollBar );
	afx_msg void OnSize(UINT nType, int cx, int cy);
private:
	CTimeOfDayDialog* m_pEditor;
	CSliderCtrlEx m_weightSlider;
	CStatic m_textMinus100,m_textPlus100,m_text0;
	bool m_bIgnoreScroll;
};

IMPLEMENT_DYNAMIC(CEnvironmentTimelineSlider, CWnd)

BEGIN_MESSAGE_MAP(CEnvironmentTimelineSlider, CWnd)
	ON_WM_HSCROLL()
	ON_WM_SIZE()
END_MESSAGE_MAP()

CEnvironmentTimelineSlider::CEnvironmentTimelineSlider(CTimeOfDayDialog* pEditor, int nID)
: m_pEditor(pEditor)
, m_bIgnoreScroll(false)
{
	VERIFY(RegisterWindowClass());

	const int style = WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
	if (!Create( ClassName(), "", style, CRect(0, 0, 0, 0), AfxGetMainWnd(), nID ))
	{
		ASSERT(0);
		return;
	}

	VERIFY(Init());
}

bool CEnvironmentTimelineSlider::RegisterWindowClass()
{
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetInstanceHandle();
	if (!::GetClassInfo(hInst, ClassName(), &wndcls))
	{
		// otherwise we need to register a new class
		wndcls.style            = 0 /*CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW*/;
		wndcls.lpfnWndProc      = ::DefWindowProc;
		wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
		wndcls.hInstance        = hInst;
		wndcls.hIcon            = NULL;
		wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
		wndcls.hbrBackground    = (HBRUSH)(COLOR_3DFACE + 1);
		wndcls.lpszMenuName     = NULL;
		wndcls.lpszClassName    = ClassName();
		if (!AfxRegisterClass(&wndcls))
			return false;
	}
	return true;
}

void CEnvironmentTimelineSlider::OnHScroll( UINT nSBCode,UINT nPos,CScrollBar* pScrollBar )
{
	if (m_bIgnoreScroll)
		return;
	if (pScrollBar == (CScrollBar*)&m_weightSlider)
	{
		float fWeight = (float)m_weightSlider.GetPos()/kSliderScale;
		m_bIgnoreScroll = true;
		m_pEditor->SetTime( fWeight );
		m_bIgnoreScroll = false;
	}
}

bool CEnvironmentTimelineSlider::Init()
{
	CRect clientRect;
	GetClientRect(&clientRect);

	VERIFY(m_weightSlider.Create( WS_CHILD|WS_VISIBLE|TBS_BOTH|TBS_AUTOTICKS|TBS_NOTICKS,CRect(0,0,1,1),this, 0));
	m_weightSlider.SetRange(0,24*kSliderScale);
	m_weightSlider.SetTicFreq( 1*kSliderScale );

	VERIFY(m_textMinus100.Create( "0",WS_CHILD|WS_VISIBLE,clientRect,this,IDC_STATIC));
	m_textMinus100.SetFont( CFont::FromHandle( (HFONT)gSettings.gui.hSystemFont) );
	VERIFY(m_textPlus100.Create( "24",WS_CHILD|WS_VISIBLE|SS_RIGHT,clientRect,this,IDC_STATIC));
	m_textPlus100.SetFont( CFont::FromHandle( (HFONT)gSettings.gui.hSystemFont) );
	VERIFY(m_text0.Create( "12",WS_CHILD|WS_VISIBLE,clientRect,this,IDC_STATIC));
	m_text0.SetFont( CFont::FromHandle( (HFONT)gSettings.gui.hSystemFont) );

	return true;
}

void CEnvironmentTimelineSlider::OnSize(UINT nType, int cx, int cy)
{
	__super::OnSize(nType,cx,cy);

	CRect rcsldr;
	GetClientRect(&rcsldr);
	const int sh = 20;

	if (!::IsWindow(m_weightSlider.GetSafeHwnd()))
		return;
	m_weightSlider.MoveWindow( CRect(rcsldr.left,rcsldr.top+10,rcsldr.right,rcsldr.top+10+sh) );
	m_textMinus100.MoveWindow( CRect(rcsldr.left+10,rcsldr.top+32,rcsldr.left+100,rcsldr.top+45) );
	m_textPlus100.MoveWindow( CRect(rcsldr.right-100,rcsldr.top+32,rcsldr.right-10,rcsldr.top+45) );
	m_text0.MoveWindow( CRect((rcsldr.right+rcsldr.left)/2-4,rcsldr.top+32,(rcsldr.right+rcsldr.left)/2+40,rcsldr.top+45) );
}

//////////////////////////////////////////////////////////////////////////
BEGIN_MESSAGE_MAP(CTimeOfDayDialog, CXTPFrameWnd)
	ON_WM_CLOSE()
	ON_WM_DESTROY()
	ON_WM_HSCROLL()
	//ON_NOTIFY_RANGE( NM_RCLICK,FIRST_SLIDER_ID,FIRST_SLIDER_ID+1000,OnSplineRClick )
	ON_NOTIFY_RANGE( SPLN_BEFORE_CHANGE,IDC_SPLINE_CTRL,IDC_SPLINE_CTRL,OnBeforeSplineChange )
	ON_NOTIFY_RANGE( SPLN_CHANGE,IDC_SPLINE_CTRL,IDC_SPLINE_CTRL,OnSplineChange )
	ON_NOTIFY_RANGE( CLRGRDN_BEFORE_CHANGE,IDC_COLOR_CTRL,IDC_COLOR_CTRL,OnBeforeSplineChange )
	ON_NOTIFY_RANGE( CLRGRDN_CHANGE,IDC_COLOR_CTRL,IDC_COLOR_CTRL,OnSplineChange )
	ON_COMMAND_RANGE( ID_TANGENT_IN_ZERO,ID_SPLINE_SNAP_GRID_Y,OnSplineCmd )

	ON_COMMAND(ID_PLAY,OnPlayAnim)
	ON_UPDATE_COMMAND_UI(ID_PLAY,OnUpdatePlayAnim)
	ON_COMMAND(ID_RECORD,OnRecord)
	ON_UPDATE_COMMAND_UI(ID_RECORD,OnUpdateRecord)
	ON_COMMAND(ID_FACEIT_PLAY_FROM_0,OnPlayAnimFrom0)
	ON_UPDATE_COMMAND_UI(ID_FACEIT_PLAY_FROM_0,OnUpdatePlayAnimFrom0)
	ON_COMMAND(ID_FACEIT_GOTO_MINUS1,OnGotoMinus1)
	ON_COMMAND(ID_FACEIT_GOTO_0,OnGoto0)
	ON_COMMAND(ID_FACEIT_GOTO_1,OnGoto1)
	ON_COMMAND(ID_HOLD,OnHold)
	ON_COMMAND(ID_FETCH,OnFetch)
	ON_COMMAND(ID_UNDO,OnUndo)
	ON_COMMAND(ID_REDO,OnRedo)

	ON_MESSAGE(XTPWM_DOCKINGPANE_NOTIFY, OnDockingPaneNotify)
	ON_MESSAGE(XTPWM_TASKPANEL_NOTIFY, OnTaskPanelNotify)

	ON_CONTROL_RANGE(TIMEOFDAYN_CHANGE, IDC_TIME_OF_DAY,IDC_TIME_OF_DAY_END, OnChangeCurrentTime)
	ON_EN_CHANGE(IDC_TIME_OF_DAY_SPEED,OnChangeTimeAnimSpeed)
	ON_NOTIFY(PROPERTYCTRL_ONSELECT,IDC_PROPERTIES_CTRL,OnPropertySelected)

	ON_COMMAND(ID_FILE_IMPORT,OnImport)
	ON_COMMAND(ID_FILE_EXPORT,OnExport)

	ON_NOTIFY(SPLN_SCROLL_ZOOM,IDC_SPLINE_CTRL,OnSplineCtrlScrollZoom)
	ON_NOTIFY(TLN_CHANGE,IDC_SPLINE_CTRL_TIMELINE,OnTimelineCtrlChange)	
	ON_NOTIFY(SPLN_TIME_CHANGE,IDC_SPLINE_CTRL,OnTimelineCtrlChange)
END_MESSAGE_MAP()

/** Undo object stored when track is modified.
*/
class CUndoTimeOfDayObject : public IUndoObject
{
public:
	CUndoTimeOfDayObject()
	{
		m_undo = XmlHelpers::CreateXmlNode("Undo");
		m_redo = XmlHelpers::CreateXmlNode("Redo");
		GetIEditor()->Get3DEngine()->GetTimeOfDay()->Serialize( m_undo,false );
	}
protected:
	virtual int GetSize() { return sizeof(*this); }
	virtual const char* GetDescription() { return "Time of Day"; };

	virtual void Undo( bool bUndo )
	{
		if (bUndo)
		{
			GetIEditor()->Get3DEngine()->GetTimeOfDay()->Serialize( m_redo,false );
		}
		GetIEditor()->Get3DEngine()->GetTimeOfDay()->Serialize( m_undo,true );
		CTimeOfDayDialog::UpdateValues();
	}
	virtual void Redo()
	{
		GetIEditor()->Get3DEngine()->GetTimeOfDay()->Serialize( m_redo,true );
		CTimeOfDayDialog::UpdateValues();
	}

private:
	XmlNodeRef m_undo;
	XmlNodeRef m_redo;
};

//////////////////////////////////////////////////////////////////////////
// Adapter for Multi element interpolators that allow to split it to several 
// different interpolators for each element separately.
//////////////////////////////////////////////////////////////////////////
class CMultiElementSplineInterpolatorAdapter : public ISplineInterpolator
{
public:
	CMultiElementSplineInterpolatorAdapter() : m_pInterpolator(0),m_element(0) {}
	CMultiElementSplineInterpolatorAdapter( ISplineInterpolator *pSpline,int element )
	{
		m_pInterpolator = pSpline;
		m_element = element;
	}
	// Dimension of the spline from 0 to 3, number of parameters used in ValueType.
	virtual int GetNumDimensions() { return m_pInterpolator->GetNumDimensions(); }
	virtual int  InsertKey( float time,ValueType value )
	{
		ValueType v = {0,0,0,0};
		v[m_element] = value[0];
		return m_pInterpolator->InsertKey(time,v);
	};
	virtual void RemoveKey( int key ) { m_pInterpolator->RemoveKey(key); };

	virtual void FindKeysInRange(float startTime, float endTime, int& firstFoundKey, int& numFoundKeys)
	{ m_pInterpolator->FindKeysInRange(startTime,endTime,firstFoundKey,numFoundKeys); }
	virtual void RemoveKeysInRange(float startTime, float endTime) { m_pInterpolator->RemoveKeysInRange(startTime,endTime); }

	virtual int   GetKeyCount() { return m_pInterpolator->GetKeyCount(); }
	virtual void  SetKeyTime( int key,float time ) { return m_pInterpolator->SetKeyTime(key,time); };
	virtual float GetKeyTime( int key ) { return m_pInterpolator->GetKeyTime(key); }

	virtual void  SetKeyValue( int key,ValueType value )
	{
		ValueType v = {0,0,0,0};
		m_pInterpolator->GetKeyValue(key,v);
		v[m_element] = value[0];
		m_pInterpolator->SetKeyValue(key,v);
	}
	virtual bool  GetKeyValue( int key,ValueType &value )
	{
		ValueType v = {0,0,0,0};
		v[m_element] = value[0];
		return m_pInterpolator->GetKeyValue(key,value);
		value[0] = v[m_element];
	}

	virtual void  SetKeyInTangent( int key,ValueType tin ) {};
	virtual void  SetKeyOutTangent( int key,ValueType tout ) {};
	virtual void  SetKeyTangents( int key,ValueType tin,ValueType tout ) {};
	virtual bool  GetKeyTangents( int key,ValueType &tin,ValueType &tout ) { return false; };

	// Changes key flags, @see ESplineKeyFlags
	virtual void  SetKeyFlags( int key,int flags ) { return m_pInterpolator->SetKeyFlags(key,flags); };
	// Retrieve key flags, @see ESplineKeyFlags
	virtual int   GetKeyFlags( int key ) { return m_pInterpolator->GetKeyFlags(key); }

	virtual void Interpolate( float time,ValueType &value )
	{
		m_pInterpolator->Interpolate(time,value);
		value[0] = value[m_element];
	};

	virtual void SerializeSpline( XmlNodeRef &node,bool bLoading ) { return m_pInterpolator->SerializeSpline(node,bLoading); };

	virtual ISplineBackup* Backup() { return m_pInterpolator->Backup(); };
	virtual void Restore(ISplineBackup* pBackup) { return m_pInterpolator->Restore(pBackup); };

public:
	ISplineInterpolator *m_pInterpolator;
	int m_element;
};

//////////////////////////////////////////////////////////////////////////
class CTimeOfDaySplineSet : public ISplineSet
{
public:
	void AddSpline( ISplineInterpolator *pSpline ) { m_splines.push_back(pSpline); };
	void RemoveAllSplines() { m_splines.clear(); }

	virtual ISplineInterpolator* GetSplineFromID(const string& id) {
		int i = atoi(id.c_str());
		if (i >= 0 && i < (int)m_splines.size())
			return m_splines[i] ;
		return 0;
	};
	virtual string GetIDFromSpline(ISplineInterpolator* pSpline)
	{
		for (int i = 0; i < (int)m_splines.size(); i++)
		{
			if (m_splines[i] == pSpline)
			{
				string s;
				s.Format("%d",i);
				return s;
			}
		}
		return "";
	}
	virtual int GetSplineCount() const { return (int)m_splines.size(); }
	virtual int GetKeyCountAtTime(float time, float threshold) const
	{
		int count = 0;
		for (int i = 0; i < (int)m_splines.size(); i++)
		{
			if (m_splines[i]->FindKey(time,threshold) > 0)
				count++;
		}
		return count;
	}

public:
	std::vector<ISplineInterpolator*> m_splines;
};

//////////////////////////////////////////////////////////////////////////
CTimeOfDayDialog::CTimeOfDayDialog()
: CXTPFrameWnd()
, m_pPropsCtrl(new CPropertyCtrl())
{
	m_pInstance = this;
	m_bAnimation = false;
	m_bPlayFrom0 = false;
	m_splineCtrlHeight = 400;
	m_bRecording = true;
	m_pSplineCtrl = 0;
	m_pColorGradientCtrl = 0;

	GetIEditor()->RegisterNotifyListener( this );

	VERIFY(RegisterWindowClass());

	const int style = WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN;
	if (!Create( ClassName(), "Environment Editor", style, CRect(0, 0, 0, 0), AfxGetMainWnd() ))
	{
		ASSERT(false);
		return;
	}

	VERIFY(Init());
}

//////////////////////////////////////////////////////////////////////////
CTimeOfDayDialog::~CTimeOfDayDialog()
{
	m_pInstance = 0;
	GetIEditor()->UnregisterNotifyListener( this );
}

bool CTimeOfDayDialog::RegisterWindowClass()
{
	WNDCLASS wndcls;
	HINSTANCE hInst = AfxGetInstanceHandle();
	if (!::GetClassInfo(hInst, ClassName(), &wndcls))
	{
		// otherwise we need to register a new class
		wndcls.style            = 0 /*CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW*/;
		wndcls.lpfnWndProc      = ::DefWindowProc;
		wndcls.cbClsExtra       = wndcls.cbWndExtra = 0;
		wndcls.hInstance        = hInst;
		wndcls.hIcon            = NULL;
		wndcls.hCursor          = AfxGetApp()->LoadStandardCursor(IDC_ARROW);
		wndcls.hbrBackground    = (HBRUSH)(COLOR_BTNFACE + 1);
		wndcls.lpszMenuName     = NULL;
		wndcls.lpszClassName    = ClassName();
		if (!AfxRegisterClass(&wndcls))
			return false;
	}
	return true;
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::PostNcDestroy()
{
	delete this;
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnDestroy()
{
	CXTPDockingPaneLayout layout( &m_paneManager );
	m_paneManager.GetLayout( &layout );
	layout.Save( _T("EnvironmentEditor") );
	CXTRegistryManager regMgr;
	regMgr.WriteProfileInt(PANE_LAYOUT_SECTION, PANE_LAYOUT_VERSION_ENTRY, kTimeOfDayDialogLayoutVersion);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnClose()
{
	DestroyWindow();
}


//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::UpdateValues()
{
	if (!m_pInstance)
		return;

	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	ITimeOfDay::SAdvancedInfo advInfo;
	pTimeOfDay->GetAdvancedInfo( advInfo );

	m_pInstance->RefreshPropertiesValues();

	m_pInstance->SetTimeRange( advInfo.fStartTime,advInfo.fEndTime,advInfo.fAnimSpeed );
	m_pInstance->SetTime( m_pInstance->GetTime(),true,true );
	m_pInstance->RedrawWindow( NULL,NULL,RDW_INVALIDATE|RDW_ERASE|RDW_ALLCHILDREN);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::RecalcLayout(BOOL bNotify)
{
	__super::RecalcLayout(bNotify);
	
	if (!IsWindow(m_wndToolBar.GetSafeHwnd()))
		return;

	CRect rc = m_paneManager.GetClientPane()->GetPaneWindowRect();
	ScreenToClient(rc);
	
	CSize sz = m_splineToolBar.CalcDockingLayout(rc.Width(), LM_HORZ|LM_HORZDOCK|LM_STRETCH, 0);
	rc.top += sz.cy;
	
	CRect rcColor(rc);
	rcColor.bottom = rc.top+40;
	rcColor.left += 40;
	if (m_pColorGradientCtrl)
		m_pColorGradientCtrl->MoveWindow(rcColor);
	rc.top = rcColor.bottom;

	CRect rcSplineRc(rc);
	if (m_pSplineCtrl)
	{
		m_pSplineCtrl->MoveWindow(rcSplineRc);
		m_pSplineCtrl->FitSplineToViewHeight();
		m_pSplineCtrl->FitSplineToViewWidth();
	}
}

//////////////////////////////////////////////////////////////////////////
bool CTimeOfDayDialog::Init()
{
	try 
	{
		if (!InitCommandBars())
			return false;
	}
	catch(CResourceException* e)
	{
		e->Delete();
		return false;
	}

	ModifyStyleEx(WS_EX_CLIENTEDGE, 0);

	CXTPCommandBars* pCommandBars = GetCommandBars();
	if (pCommandBars == NULL)
	{
		TRACE0("Failed to create command bars object.\n");
		return false;
	}

	CRect clientRect;
	GetClientRect(&clientRect);

	//////////////////////////////////////////////////////////////////////////
	VERIFY(m_wndToolBar.CreateToolBar(WS_VISIBLE|WS_CHILD|CBRS_TOOLTIPS|CBRS_ALIGN_TOP, this, AFX_IDW_TOOLBAR));
	VERIFY(m_wndToolBar.LoadToolBar(IDR_TIME_OF_DAY_BAR));
	m_wndToolBar.SetFlags(xtpFlagAlignTop|xtpFlagStretched);
	m_wndToolBar.EnableCustomization(FALSE);

	m_timelineCtrl.Create( WS_CHILD|WS_VISIBLE,CRect(0,0,1,1),this,IDC_SPLINE_CTRL_TIMELINE );
	m_timelineCtrl.SetTimeRange( Range(0,1) );
	m_timelineCtrl.SetTicksTextScale( 24.0f );

	// TODO: remove this copy-paste
	m_paneManager.InstallDockingPanes(this);
	m_paneManager.SetTheme(xtpPaneThemeOffice2003);
	m_paneManager.SetThemedFloatingFrames(TRUE);

	// It is important to create this toolbar after installing docking panes
	VERIFY(m_splineToolBar.CreateToolBar(WS_VISIBLE|WS_CHILD|CBRS_TOOLTIPS|CBRS_ALIGN_BOTTOM, this, IDC_SPLINE_TOOLBAR));
	VERIFY(m_splineToolBar.LoadToolBar(IDR_SPLINE_EDIT_BAR));
	m_splineToolBar.SetFlags(xtpFlagAlignTop|xtpFlagStretched);
	m_splineToolBar.EnableCustomization(FALSE);
	for (int i = 0; i < m_splineToolBar.GetControlCount(); ++i)
	{
		// remove useless buttons
		if (m_splineToolBar.GetControl(i)->GetID() > ID_SPLINE_FIT_Y)
		{
			m_splineToolBar.GetControls()->Remove(i);
			--i;
		}
	}

	VERIFY(CreatePanes());

	// these properties will be shown when Basic properties for Time Of Day will be enabled
	m_basicPropertyDisplayNames.insert( "Sun color" );
	m_basicPropertyDisplayNames.insert( "Sun color multiplier" );
	m_basicPropertyDisplayNames.insert( "Sky color" );
	m_basicPropertyDisplayNames.insert( "Sky color multiplier" );
	m_basicPropertyDisplayNames.insert( "Fog color" );
	m_basicPropertyDisplayNames.insert( "Fog color multiplier" );
	m_basicPropertyDisplayNames.insert( "Global density" );
	m_basicPropertyDisplayNames.insert( "Sun intensity" );
	m_basicPropertyDisplayNames.insert( "Sun intensity multiplier" );
	m_basicPropertyDisplayNames.insert( "Star intensity" );
	m_basicPropertyDisplayNames.insert( "Moon color" );
	m_basicPropertyDisplayNames.insert( "Sun rays visibility" );
	m_basicPropertyDisplayNames.insert( "Saturation" );
	m_basicPropertyDisplayNames.insert( "Contrast" );
	m_basicPropertyDisplayNames.insert( "Brightness" );

	{
		m_pSplineCtrl = new CSplineCtrlContainerTOD;

		m_pSplineCtrl->Create( WS_CHILD|WS_VISIBLE|WS_BORDER|WS_CLIPSIBLINGS|WS_CLIPCHILDREN,CRect(0, 0, 550, 400),this,IDC_SPLINE_CTRL );
		m_pSplineCtrl->ModifyStyleEx( 0,WS_EX_CLIENTEDGE );
		m_pSplineCtrl->SetDefaultKeyTangentType(SPLINE_KEY_TANGENT_LINEAR);

		m_pSplineCtrl->SetTimeRange( Range(0,1.0f) );
		m_pSplineCtrl->SetValueRange( Range(-1.0f,1.0f) );
		m_pSplineCtrl->SetMinTimeEpsilon( 0.00001f );
		m_pSplineCtrl->SetTimelineCtrl(&m_timelineCtrl);
		m_pSplineCtrl->SetTooltipValueScale(24,1);
	}

	{
		CColorGradientCtrl *pColorCtrl = new CColorGradientCtrl;

		CRect sliderRc(0,0,1,40);
		pColorCtrl->Create( WS_CHILD|WS_VISIBLE|WS_BORDER|WS_CLIPSIBLINGS|WS_CLIPCHILDREN,sliderRc,this,IDC_COLOR_CTRL );
		pColorCtrl->ModifyStyleEx( 0,WS_EX_CLIENTEDGE );

		pColorCtrl->SetNoZoom(false);
		pColorCtrl->SetTimeRange( 0,1.0f );
		pColorCtrl->LockFirstAndLastKeys(true);
		pColorCtrl->SetTooltipValueScale(24,1);

		m_pColorGradientCtrl = pColorCtrl;
	}

	CreateProperties();	

	RecalcLayout();
	UpdateValues();
	SetTime( GetTime(),true );
	
	CXTRegistryManager regMgr;
	int paneLayoutVersion = regMgr.GetProfileInt(PANE_LAYOUT_SECTION, PANE_LAYOUT_VERSION_ENTRY, 0);
	if (paneLayoutVersion == kTimeOfDayDialogLayoutVersion)
	{
		CXTPDockingPaneLayout layout( &m_paneManager );
		if ( layout.Load( _T("EnvironmentEditor") ) )
			m_paneManager.SetLayout( &layout );
	}

	// by default show only basic properties
	m_bShowOnlyBasicProperties = gSettings.bShowBasicPropertiesInTimeOfDay;
	OnToggleBasicAdvancedProperties();
	ResetSpline(0);

	m_pHDRPane->UpdateFilmCurve();

	return true;
}

//////////////////////////////////////////////////////////////////////////
bool CTimeOfDayDialog::CreatePanes()
{
	CRect clientRect;
	GetClientRect(&clientRect);


	{
		CXTPDockingPane* pDockPane = m_paneManager.CreatePane(IDW_TIMELINE_PANE,
																													CRect(0, 0, 350, 80),
																													dockBottomOf);
		pDockPane->SetTitle("Timeline");
		pDockPane->SetOptions(xtpPaneNoCloseable | xtpPaneNoFloatable);

		m_pTimelineSlider.reset(new CEnvironmentTimelineSlider(this, IDC_TIMELINE_SLIDER));
	}

	{
		const int taskPaneWidth = 200;
		CXTPDockingPane* pDockPane = m_paneManager.CreatePane(IDW_TASKS_PANE,
																													CRect(0, 0, taskPaneWidth, 400),
																													dockLeftOf);
		pDockPane->SetTitle("Tasks");
		pDockPane->SetOptions(xtpPaneNoCloseable | xtpPaneNoFloatable);

		m_wndTaskPanel.Create( WS_CHILD|WS_BORDER|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN,
													 clientRect, this, IDC_TASKPANEL );
		m_wndTaskPanel.SetBehaviour(xtpTaskPanelBehaviourExplorer);
		m_wndTaskPanel.SetTheme(xtpTaskPanelThemeNativeWinXP);
		m_wndTaskPanel.SetSelectItemOnFocus(TRUE);
		m_wndTaskPanel.AllowDrag(TRUE);
		m_wndTaskPanel.SetAnimation(xtpTaskPanelAnimationNo);

		// Add default tasks.
		CXTPTaskPanelGroupItem *pItem =  NULL;
		CXTPTaskPanelGroup *pGroup = NULL;
		{
			pGroup = m_wndTaskPanel.AddGroup(1);
			pGroup->SetCaption( "Time of Day Tasks" );

			pItem = pGroup->AddLinkItem(ID_FILE_IMPORT);
			pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Import From File" );

			pItem = pGroup->AddLinkItem(ID_FILE_EXPORT);
			pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Export To File" );

			pItem = pGroup->AddLinkItem(ID_RESET_TO_DEFAULT);
			pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Reset Values" );

			pItem = pGroup->AddLinkItem(ID_EXPAND_ALL_SPLINES);
			pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Expand All" );

			pItem = pGroup->AddLinkItem(ID_COLLAPSE_ALL_SPLINES);
			pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Collapse All" );

			pItem = pGroup->AddLinkItem(ID_TOGGLE_ADVANCED_PROPERTIES);
			pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Toggle Advanced Properties" );
		}

		m_timeEdit.Create( WS_CHILD|WS_VISIBLE|ES_CENTER,CRect(0,0,taskPaneWidth,16),this,IDC_TIME_OF_DAY );
		m_timeEdit.SetFont( CFont::FromHandle((HFONT)gSettings.gui.hSystemFont) );
		m_timeEdit.SetParent(&m_wndTaskPanel);
		m_timeEdit.SetOwner(this);

		m_timeEditStart.Create( WS_CHILD|WS_VISIBLE|ES_CENTER,CRect(0,0,taskPaneWidth,16),this,IDC_TIME_OF_DAY_START );
		m_timeEditStart.SetFont( CFont::FromHandle((HFONT)gSettings.gui.hSystemFont) );
		m_timeEditStart.SetParent(&m_wndTaskPanel);
		m_timeEditStart.SetOwner(this);

		m_timeEditEnd.Create( WS_CHILD|WS_VISIBLE|ES_CENTER,CRect(0,0,taskPaneWidth,16),this,IDC_TIME_OF_DAY_END );
		m_timeEditEnd.SetFont( CFont::FromHandle((HFONT)gSettings.gui.hSystemFont) );
		m_timeEditEnd.SetParent(&m_wndTaskPanel);
		m_timeEditEnd.SetOwner(this);

		m_timeAnimSpeedCtrl.Create( this,CRect(0,0,taskPaneWidth,18),IDC_TIME_OF_DAY_SPEED,CNumberCtrl::NOBORDER|CNumberCtrl::CENTER_ALIGN );
		m_timeAnimSpeedCtrl.SetParent(&m_wndTaskPanel);
		m_timeAnimSpeedCtrl.SetOwner(this);
		{
			pGroup = m_wndTaskPanel.AddGroup(2);
			pGroup->SetCaption( "Current Time" );

			pItem =  pGroup->AddControlItem(m_timeEdit);
			pItem->SetCaption( "Time: " );

			pItem =  pGroup->AddTextItem(_T("Start Time:"));
			pItem =  pGroup->AddControlItem(m_timeEditStart);
			pItem =  pGroup->AddTextItem(_T("End Time:"));
			pItem =  pGroup->AddControlItem(m_timeEditEnd);
			pItem =  pGroup->AddTextItem(_T("Play Speed:"));
			pItem =  pGroup->AddControlItem(m_timeAnimSpeedCtrl);
		}

		m_forceUpdateCheck.Create( "Force sky update",WS_CHILD|WS_VISIBLE|BS_AUTOCHECKBOX,CRect(0,0,60,16),&m_wndTaskPanel,IDC_SKYUPDATE_CHECKBOX );
		m_forceUpdateCheck.SetCheck( gSettings.bForceSkyUpdate );
		m_forceUpdateCheck.SetFont( CFont::FromHandle( (HFONT)gSettings.gui.hSystemFont) );
		{
			pGroup = m_wndTaskPanel.AddGroup(3);
			pGroup->SetCaption( "Update Tasks" );

			pItem =  pGroup->AddLinkItem(ID_PLAY_TOD_ANIM); pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Play" );
			pItem =  pGroup->AddLinkItem(ID_STOP_TOD_ANIM); pItem->SetType(xtpTaskItemTypeLink);
			pItem->SetCaption( "Stop" );
			pItem =  pGroup->AddControlItem(m_forceUpdateCheck);
		}

		//////////////////////////////////////////////////////////////////////////
	}

	{
		CXTPDockingPane* pDockPane = m_paneManager.CreatePane(IDW_PROPS_PANE,
																													CRect(0, 0, 250, 80),
																													dockRightOf);
		pDockPane->SetTitle("Parameters");
		pDockPane->SetOptions(xtpPaneNoCloseable | xtpPaneNoFloatable);

		m_pPropsCtrl->Create( WS_CHILD|WS_VISIBLE,CRect(0,0,100,100),this,IDC_PROPERTIES_CTRL );
		m_pPropsCtrl->ModifyStyleEx( 0,WS_EX_CLIENTEDGE );
		m_pPropsCtrl->SetUpdateCallback( functor(*this,&CTimeOfDayDialog::OnUpdateProperties) );
	}

	{
		CXTPDockingPane* pDockPane = m_paneManager.CreatePane(IDW_HDR_PANE,
																													CRect(0, 0, 200, 400),
																													dockLeftOf);
		pDockPane->SetTitle("HDR Settings");
		pDockPane->SetOptions(xtpPaneNoCloseable | xtpPaneNoFloatable);

		m_pHDRPane.reset(new CHDRPane(this, IDC_HDR_PANE));
	}

	return true;

}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::CreateProperties()
{
	m_pVars = new CVarBlock;
	m_pHDRPane->m_pVars = new CVarBlock;

	std::map<CString,IVariable*> groups;

	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	for (int i = 0; i < pTimeOfDay->GetVariableCount(); i++)
	{
		ITimeOfDay::SVariableInfo varInfo;
		if (!pTimeOfDay->GetVariableInfo( i,varInfo ))
			continue;

		if (!varInfo.pInterpolator)
			continue;

		IVariable *pGroupVar = stl::find_in_map(groups,varInfo.group,0);
		if (!pGroupVar)
		{
			// Create new group
			pGroupVar = new CVariableArray;
			pGroupVar->SetName( varInfo.group );
			if (strcmp(varInfo.group, "HDR") == 0) // HDR parameters should go into the separate HDR pane.
				m_pHDRPane->m_pVars->AddVariable( pGroupVar );
			else
				m_pVars->AddVariable( pGroupVar );
			groups[varInfo.group] = pGroupVar;
		}

		IVariable *pVar = 0;
		if (varInfo.type == ITimeOfDay::TYPE_COLOR)
		{
			//////////////////////////////////////////////////////////////////////////
			// Add Var.
			pVar = new CVariable<Vec3>;
			pVar->SetDataType( IVariable::DT_COLOR );
			pVar->Set( Vec3(varInfo.fValue[0],varInfo.fValue[1],varInfo.fValue[2]) );
		}
		else if (varInfo.type == ITimeOfDay::TYPE_FLOAT)
		{
			// Add Var.
			pVar = new CVariable<float>;
			pVar->Set( varInfo.fValue[0] );
			pVar->SetLimits( varInfo.fValue[1],varInfo.fValue[2] );
		}

		if (pVar)
		{
			pVar->SetName( varInfo.name );
			pVar->SetHumanName( varInfo.displayName );
			pGroupVar->AddChildVar( pVar );
			pVar->SetUserData((void*)i);
		}
	}

	m_pPropsCtrl->EnableUpdateCallback(false);
	m_pPropsCtrl->AddVarBlock( m_pVars );
	m_pPropsCtrl->ExpandAll();	
	m_pPropsCtrl->EnableNotifyWithoutValueChange(true);
	m_pPropsCtrl->EnableUpdateCallback(true);

	m_pHDRPane->m_propsCtrl.EnableUpdateCallback(false);
	m_pHDRPane->m_propsCtrl.AddVarBlock( m_pHDRPane->m_pVars );
	m_pHDRPane->m_propsCtrl.ExpandAll();	
	m_pHDRPane->m_propsCtrl.EnableNotifyWithoutValueChange(true);
	m_pHDRPane->m_propsCtrl.EnableUpdateCallback(true);
}


//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnSplineRClick( UINT nID, NMHDR* pNMHDR, LRESULT* lpResult )
{

}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnBeforeSplineChange( UINT nID, NMHDR* pNMHDR, LRESULT* lpResult )
{
	if (CUndo::IsRecording())
		CUndo::Record( new CUndoTimeOfDayObject );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnSplineChange( UINT nID, NMHDR* pNMHDR, LRESULT* lpResult )
{
	RefreshPropertiesValues();

	CWnd *pWnd = CWnd::FromHandle(pNMHDR->hwndFrom);
	if (pWnd->IsKindOf(RUNTIME_CLASS(CSplineCtrlEx)))
	{
		CSplineCtrlEx *pSlineCtrl = (CSplineCtrlEx*)pWnd;

		// Spline changes
		if (m_pColorGradientCtrl)
			m_pColorGradientCtrl->Invalidate();
	}
	else
	{
		// Color control changes.
		if (m_pSplineCtrl)
			m_pSplineCtrl->Invalidate();
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnSplineCmd( UINT nCmd )
{
	if (m_pSplineCtrl)
		m_pSplineCtrl->OnUserCommand(nCmd);
}

//////////////////////////////////////////////////////////////////////////
float CTimeOfDayDialog::GetTime() const
{
	float fTime = 0;
	if (GetIEditor()->GetDocument()->GetCurrentMission())
		fTime = GetIEditor()->GetDocument()->GetCurrentMission()->GetTime();
	return fTime;
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::SetTimeRange( float fTimeStart,float fTimeEnd,float fSpeed )
{
	{
		int nHour = floor(fTimeStart);
		int nMins = (fTimeStart - floor(fTimeStart)) * 60.0f;
		m_timeEditStart.SetTime( nHour,nMins );
	}
	{
		int nHour = floor(fTimeEnd);
		int nMins = (fTimeEnd - floor(fTimeEnd)) * 60.0f;
		m_timeEditEnd.SetTime( nHour,nMins );
	}
	m_timeAnimSpeedCtrl.SetValue( fSpeed );

	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	ITimeOfDay::SAdvancedInfo advInfo;
	pTimeOfDay->GetAdvancedInfo( advInfo );
	advInfo.fStartTime = fTimeStart;
	advInfo.fEndTime = fTimeEnd;
	advInfo.fAnimSpeed = fSpeed;
	pTimeOfDay->SetAdvancedInfo( advInfo );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::RefreshPropertiesValues( bool bSetTime )
{
	m_pPropsCtrl->EnableUpdateCallback(false);
	m_pHDRPane->m_propsCtrl.EnableUpdateCallback(false);

	// Interpolate internal values
	if (bSetTime)
		gEnv->p3DEngine->GetTimeOfDay()->SetTime( GetTime(),false );

	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	for (int i = 0,numVars = pTimeOfDay->GetVariableCount(); i < numVars; i++)
	{
		ITimeOfDay::SVariableInfo varInfo;
		if (!pTimeOfDay->GetVariableInfo( i,varInfo ))
			continue;
		IVariable *pVar = FindVariable(varInfo.name);

		if (!pVar)
			continue;

		switch (varInfo.type)
		{
		case ITimeOfDay::TYPE_FLOAT:
			pVar->Set( varInfo.fValue[0] );
			break;
		case ITimeOfDay::TYPE_COLOR:
			pVar->Set( Vec3(varInfo.fValue[0],varInfo.fValue[1],varInfo.fValue[2]) );
			break;
		}
	}
	m_pPropsCtrl->EnableUpdateCallback(true);
	m_pHDRPane->m_propsCtrl.EnableUpdateCallback(true);
	
	m_pHDRPane->UpdateFilmCurve();

	// Notify that time of day values changed.
	GetIEditor()->Notify( eNotify_OnTimeOfDayChange );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::SetTime( float fHour,bool bOnlyUpdateUI,bool bNoPropertiesUpdate )
{
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();

	bool bForceUpdate = m_forceUpdateCheck.GetCheck() == BST_CHECKED;

	bool bTimeWasSet = false;
	if (!bOnlyUpdateUI)
	{
		pTimeOfDay->SetTime( fHour,bForceUpdate );
		bTimeWasSet = true;
	}

	if (GetIEditor()->GetDocument()->GetCurrentMission())
		GetIEditor()->GetDocument()->GetCurrentMission()->SetTime( fHour );

	m_pTimelineSlider->SetTime(fHour);

	//if (m_timelineCtrl)
	//m_timelineCtrl.SetTimeMarker(fHour/24.0f);

	int nHour = floor(fHour);
	int nMins = (fHour - floor(fHour)) * 60.0f;
	m_timeEdit.SetTime( nHour,nMins );

	if (m_pSplineCtrl)
		m_pSplineCtrl->SetTimeMarker(fHour/24.0f);
	if (m_pColorGradientCtrl)
		m_pColorGradientCtrl->SetTimeMarker(fHour/24.0f);

	if (!bNoPropertiesUpdate)
	{
		RefreshPropertiesValues(!bTimeWasSet);
	}
	else
		GetIEditor()->Notify( eNotify_OnTimeOfDayChange );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnPlayAnim()
{
	m_bAnimation = !m_bAnimation;
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnUpdatePlayAnim( CCmdUI* pCmdUI )
{
	pCmdUI->SetCheck( (m_bAnimation)?BST_CHECKED:BST_UNCHECKED );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnRecord()
{
	m_bRecording = !m_bRecording;
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnUpdateRecord( CCmdUI* pCmdUI )
{
	pCmdUI->SetCheck( (m_bRecording)?BST_CHECKED:BST_UNCHECKED );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnPlayAnimFrom0()
{
	m_bPlayFrom0 = !m_bPlayFrom0;
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnUpdatePlayAnimFrom0( CCmdUI* pCmdUI )
{
	pCmdUI->SetCheck(m_bPlayFrom0);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnGotoMinus1()
{
	SetTime(0);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnGoto0()
{
	SetTime(12);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnGoto1()
{
	SetTime(24);
}

void CTimeOfDayDialog::OnToggleBasicAdvancedProperties()
{
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();

	//
	// show / hide advanced properties
	//
	for (int i = 0,numVars = pTimeOfDay->GetVariableCount(); i < numVars; i++)
	{
		ITimeOfDay::SVariableInfo varInfo;

		if (!pTimeOfDay->GetVariableInfo( i,varInfo ))
			continue;

		IVariable *pVar = FindVariable(varInfo.name);
		
		if (!pVar)
			continue;

		bool bPropIsVisible = true;

		// skip advanced properties 
		if( m_bShowOnlyBasicProperties && !stl::find( m_basicPropertyDisplayNames, varInfo.displayName ) )
			bPropIsVisible = false;

		int flags = pVar->GetFlags();
		flags = bPropIsVisible ? ( flags & ( ~IVariable::UI_INVISIBLE ) ) : ( flags | IVariable::UI_INVISIBLE );
		pVar->SetFlags( flags );
	}

	//
	// show / hide empty property groups ( with all children hidden )
	//
	for (int i = 0, numVars = m_pVars->GetVarsCount(); i < numVars; i++)
	{
		IVariable *pVar = m_pVars->GetVariable( i );

		if( !pVar || IVariable::ARRAY != pVar->GetType() )
			continue;

		// if this is a group
		CVariableArray* pVarGroup = (CVariableArray*)pVar;
		bool bGroupIsVisible = false;

		// check all its children to see if we have any one of them visible
		for( int j = 0; j < pVarGroup->NumChildVars(); ++j )
		{
			if( !(pVarGroup->GetChildVar( j )->GetFlags() & IVariable::UI_INVISIBLE) )
			{
				bGroupIsVisible = true;
				break;
			}
		}

		int flags = pVarGroup->GetFlags();
		flags = bGroupIsVisible ? ( flags & ( ~IVariable::UI_INVISIBLE ) ) : ( flags | IVariable::UI_INVISIBLE );
		pVarGroup->SetFlags( flags );
	}

	// refresh properties
	m_pPropsCtrl->EnableUpdateCallback(false);
	m_pPropsCtrl->UpdateVarBlock( m_pVars );
	m_pPropsCtrl->RedrawWindow();
	m_pPropsCtrl->EnableNotifyWithoutValueChange(true);
	m_pPropsCtrl->EnableUpdateCallback(true);

	m_pHDRPane->m_propsCtrl.EnableUpdateCallback(false);
	m_pHDRPane->m_propsCtrl.UpdateVarBlock( m_pHDRPane->m_pVars );
	m_pHDRPane->m_propsCtrl.RedrawWindow();
	m_pHDRPane->m_propsCtrl.EnableNotifyWithoutValueChange(true);
	m_pHDRPane->m_propsCtrl.EnableUpdateCallback(true);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnEditorNotifyEvent( EEditorNotifyEvent event )
{
	switch (event)
	{
	case eNotify_OnBeginSceneOpen:
		{
			// prevent crash during redraw which can happen before eNotify_OnEndSceneOpen
			m_pSplineCtrl->RemoveAllSplines(); 
			m_pColorGradientCtrl->SetSpline(0);
		}
		break;
	case eNotify_OnEndSceneOpen:
	case eNotify_OnEndNewScene:
		{
			UpdateValues();

			m_pHDRPane->m_propsCtrl.ClearSelection();
			if (CPropertyItem* pSelectedItem = m_pPropsCtrl->GetSelectedItem())
			{
				if (IVariable* pVar = pSelectedItem->GetVariable())
					ResetSpline(pVar);
			}
		}
		break;

	case eNotify_OnIdleUpdate:
		if (m_bAnimation)
		{
			ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
			float fHour = pTimeOfDay->GetTime();

			ITimeOfDay::SAdvancedInfo advInfo;
			pTimeOfDay->GetAdvancedInfo( advInfo );
			// get the TOD cycle speed from UI
			advInfo.fAnimSpeed = m_timeAnimSpeedCtrl.GetValue();
			float dt = gEnv->pTimer->GetFrameTime();
			float fTime = fHour + dt*advInfo.fAnimSpeed;
			if (fTime > advInfo.fEndTime)
				fTime = advInfo.fStartTime;
			if (fTime < advInfo.fStartTime)
				fTime = advInfo.fEndTime;
			if (m_bPlayFrom0 && fTime < 0)
				fTime = 0;
			SetTime( fTime );
		}
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnTimelineCtrlChange( NMHDR* pNMHDR, LRESULT* pResult )
{
	float fTime = m_timelineCtrl.GetTimeMarker();
	SetTime(fTime*24.0f);
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnChangeCurrentTime( UINT nID )
{
	switch (nID)
	{
	case IDC_TIME_OF_DAY:
		{
			CString str;
			m_timeEdit.GetWindowText(str);
			int h,m;
			sscanf( str,_T("%02d:%02d"),&h,&m );
			float fTime = (float)h + (float)m/60.0f;
			SetTime( fTime );
		}
		break;
	case IDC_TIME_OF_DAY_START:
		{
			CString str;
			m_timeEditStart.GetWindowText(str);
			int h,m;
			sscanf( str,_T("%02d:%02d"),&h,&m );
			float fTime = (float)h + (float)m/60.0f;
			ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
			ITimeOfDay::SAdvancedInfo advInfo;
			pTimeOfDay->GetAdvancedInfo( advInfo );
			advInfo.fStartTime = fTime;
			pTimeOfDay->SetAdvancedInfo( advInfo );
		}
		break;
	case IDC_TIME_OF_DAY_END:
		{
			CString str;
			m_timeEditEnd.GetWindowText(str);
			int h,m;
			sscanf( str,_T("%02d:%02d"),&h,&m );
			float fTime = (float)h + (float)m/60.0f;
			ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
			ITimeOfDay::SAdvancedInfo advInfo;
			pTimeOfDay->GetAdvancedInfo( advInfo );
			advInfo.fEndTime = fTime;
			pTimeOfDay->SetAdvancedInfo( advInfo );
		}
		break;
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnChangeTimeAnimSpeed()
{
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	ITimeOfDay::SAdvancedInfo advInfo;
	pTimeOfDay->GetAdvancedInfo( advInfo );
	advInfo.fAnimSpeed = m_timeAnimSpeedCtrl.GetValue();
	pTimeOfDay->SetAdvancedInfo( advInfo );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnSplineCtrlScrollZoom( NMHDR* pNMHDR, LRESULT* pResult )
{
	if (m_pSplineCtrl && m_pColorGradientCtrl)
	{
		m_pColorGradientCtrl->SetZoom( m_pSplineCtrl->GetZoom().x );
		m_pColorGradientCtrl->SetOrigin( m_pSplineCtrl->GetScrollOffset().x );
		m_pColorGradientCtrl->RedrawWindow();
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnImport()
{
	char szFilters[] = "Time Of Day Settings (*.xml)|*.xml|Time Of Day Settings Old (*.tod)|*.tod||";
	CString fileName;

	if (CFileUtil::SelectFile( szFilters,GetIEditor()->GetLevelFolder(),fileName ))
	{
		XmlNodeRef root = GetISystem()->LoadXmlFromFile( fileName );
		if (root)
		{
			ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
			float fTime = GetTime();
			pTimeOfDay->Serialize( root,true );
			pTimeOfDay->SetTime( fTime,true );

			UpdateValues();
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnExport()
{
	char szFilters[] = "Time Of Day Settings (*.xml)|*.xml||";
	CString fileName;
	if (CFileUtil::SelectSaveFile( szFilters, "xml", GetIEditor()->GetLevelFolder(), fileName))
	{
		// Write the light settings into the archive
		XmlNodeRef node = XmlHelpers::CreateXmlNode( "TimeOfDay" );
		ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
		pTimeOfDay->Serialize( node,false );
		XmlHelpers::SaveXmlNode( node,fileName );
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnExpandAll()
{
	m_pPropsCtrl->ExpandAll();
	m_pHDRPane->m_propsCtrl.ExpandAll();
}
//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnResetToDefaultValues()
{
	if (!m_pInstance)
		return;

	if (MessageBox("Are you sure you want to reset all values to their default values?","Reset values",MB_YESNO)==IDYES)
	{
		ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
		pTimeOfDay->ResetVariables();
		RefreshPropertiesValues();		
		m_pHDRPane->m_propsCtrl.ClearSelection();
		CPropertyItem* selectedItem = m_pPropsCtrl->GetSelectedItem();
		if( selectedItem )
		{
			IVariable* var = selectedItem->GetVariable();
			if( var )
				ResetSpline(var);
		}
	}
}
//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnCollapseAll()
{
	// Must implement.
}

//////////////////////////////////////////////////////////////////////////
LRESULT CTimeOfDayDialog::OnTaskPanelNotify( WPARAM wParam, LPARAM lParam )
{
	switch(wParam) {
case XTP_TPN_CLICK:
	{
		CXTPTaskPanelGroupItem* pItem = (CXTPTaskPanelGroupItem*)lParam;
		UINT nCmdID = pItem->GetID();
		switch (nCmdID)
		{
		case ID_PLAY_TOD_ANIM:
			m_bAnimation = true;
			break;
		case ID_STOP_TOD_ANIM:
			m_bAnimation = false;
			break;
		case ID_FILE_IMPORT:
			OnImport();
			break;
		case ID_FILE_EXPORT:
			OnExport();
			break;

		case ID_RESET_TO_DEFAULT:
			OnResetToDefaultValues();
			break;

		case ID_EXPAND_ALL_SPLINES:
			OnExpandAll();
			break;
		case ID_COLLAPSE_ALL_SPLINES:
			OnCollapseAll();
			break;
		case ID_TOGGLE_ADVANCED_PROPERTIES:
			m_bShowOnlyBasicProperties = !m_bShowOnlyBasicProperties;
			gSettings.bShowBasicPropertiesInTimeOfDay = m_bShowOnlyBasicProperties;
			OnToggleBasicAdvancedProperties();
			break;
		}
	}
	break;

case XTP_TPN_RCLICK:
	break;
	}
	return 0;
}

//////////////////////////////////////////////////////////////////////////
LRESULT CTimeOfDayDialog::OnDockingPaneNotify(WPARAM wParam, LPARAM lParam)
{
	if ( wParam == XTP_DPN_SHOWWINDOW )
	{
		// get a pointer to the docking pane being shown.
		CXTPDockingPane* pwndDockWindow = (CXTPDockingPane*) lParam;
		if ( !pwndDockWindow->IsValid() )
		{
			switch ( pwndDockWindow->GetID() )
			{
			case IDW_TASKS_PANE:
				pwndDockWindow->Attach( &m_wndTaskPanel );
				m_wndTaskPanel.SetOwner( this );
				break;
			case IDW_PROPS_PANE:
				pwndDockWindow->Attach( m_pPropsCtrl.get() );
				m_pPropsCtrl->SetOwner( this );
				break;
			case IDW_TIMELINE_PANE:
				pwndDockWindow->Attach( m_pTimelineSlider.get() );
				m_pTimelineSlider->SetOwner( this );
				break;
			case IDW_HDR_PANE:
				pwndDockWindow->Attach( m_pHDRPane.get() );
				m_pHDRPane->SetOwner( this );
				break;
			default:
				return FALSE;
			}
		}
		return TRUE;
	}

	return FALSE;
}


//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnUpdateProperties( IVariable *pVar )
{
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();

	int nIndex = -1; 
	if (pVar)
		nIndex = (int)pVar->GetUserData();

	if (nIndex != -1)
	{
		ITimeOfDay::SVariableInfo varInfo;
		if (pTimeOfDay->GetVariableInfo(nIndex, varInfo))
		{
			float fTime = GetTime();
			float fSplineTime = fTime / 24;

			const float cNearestKeySearchEpsilon = 0.00001f;
			int nKey = varInfo.pInterpolator->FindKey(fSplineTime, cNearestKeySearchEpsilon );
			int nLastKey = varInfo.pInterpolator->GetKeyCount()-1;

			if (CUndo::IsRecording())
				CUndo::Record( new CUndoTimeOfDayObject );

			switch (varInfo.type)
			{
			case ITimeOfDay::TYPE_FLOAT:
			{
				float fVal = 0;
				pVar->Get(fVal);
				if (m_bRecording)
				{
					if (nKey < 0)
						nKey = varInfo.pInterpolator->InsertKeyFloat(fSplineTime,fVal);
					else
					{
						varInfo.pInterpolator->SetKeyValueFloat(nKey,fVal);
						if (nKey == 0)
							varInfo.pInterpolator->SetKeyValueFloat(nLastKey,fVal);
						else if (nKey == nLastKey)
							varInfo.pInterpolator->SetKeyValueFloat(0,fVal);
					}
					if (m_pSplineCtrl && m_pSplineCtrl->GetDefaultKeyTangentType() != SPLINE_KEY_TANGENT_NONE)
						SetKeyTangentType(varInfo.pInterpolator, nKey, m_pSplineCtrl->GetDefaultKeyTangentType());
				}

				float v3[3] = {fVal,varInfo.fValue[1],varInfo.fValue[2]};
				pTimeOfDay->SetVariableValue( nIndex,v3 );
			}
			break;
			case ITimeOfDay::TYPE_COLOR:
			{
				Vec3 vVal;
				pVar->Get(vVal);
				float v3[3] = {vVal.x,vVal.y,vVal.z};
				if (m_bRecording)
				{
					if (nKey < 0)
						nKey = varInfo.pInterpolator->InsertKeyFloat3(fSplineTime,v3);
					else
					{
						varInfo.pInterpolator->SetKeyValueFloat3(nKey,v3);
						if (nKey == 0)
							varInfo.pInterpolator->SetKeyValueFloat3(nLastKey,v3);
						else if (nKey == nLastKey)
							varInfo.pInterpolator->SetKeyValueFloat3(0,v3);
					}
					if (m_pSplineCtrl && m_pSplineCtrl->GetDefaultKeyTangentType() != SPLINE_KEY_TANGENT_NONE)
						SetKeyTangentType(varInfo.pInterpolator, nKey, m_pSplineCtrl->GetDefaultKeyTangentType());
				}
				pTimeOfDay->SetVariableValue( nIndex,v3 );
				if (m_pColorGradientCtrl)
					m_pColorGradientCtrl->Invalidate();
			}
			break;
			}

			if (m_pSplineCtrl)
				m_pSplineCtrl->Invalidate();

			if (varInfo.nParamId == ITimeOfDay::PARAM_HDR_FILMCURVE_SHOULDER_SCALE
				|| varInfo.nParamId == ITimeOfDay::PARAM_HDR_FILMCURVE_LINEAR_SCALE
				|| varInfo.nParamId == ITimeOfDay::PARAM_HDR_FILMCURVE_TOE_SCALE
				|| varInfo.nParamId == ITimeOfDay::PARAM_HDR_FILMCURVE_WHITEPOINT)
				m_pHDRPane->UpdateFilmCurve();

			bool bForceUpdate = m_forceUpdateCheck.GetCheck() == BST_CHECKED;
			pTimeOfDay->Update( false,bForceUpdate );

			GetIEditor()->Notify( eNotify_OnTimeOfDayChange );

			GetIEditor()->GetConsoleSync()->SyncTimeOfDay();
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnPropertySelected( NMHDR* pNMHDR, LRESULT* pResult )
{
	IVariable *pVar = nullptr;
	CPropertyItem* pSelected = m_pPropsCtrl->GetSelectedItem();
	if (pSelected)
	{
		m_pHDRPane->m_propsCtrl.ClearSelection();
		pVar = pSelected->GetVariable();
	}

	if (pVar && pVar->GetType() == IVariable::ARRAY)
		pVar = nullptr;

	ResetSpline(pVar);

	if (m_pSplineCtrl)
	{
		m_pSplineCtrl->FitSplineToViewHeight();
		m_pSplineCtrl->FitSplineToViewWidth();
	}
}


void CTimeOfDayDialog::ResetSpline( IVariable* pVar )
{
	if (pVar)
	{
	ITimeOfDay *pTimeOfDay = gEnv->p3DEngine->GetTimeOfDay();
	ITimeOfDay::SVariableInfo varInfo;
		int index = (int)pVar->GetUserData();
	if( !pTimeOfDay->GetVariableInfo( index, varInfo ) )
		return;

	m_pSplineCtrl->SetTimeRange( Range(0,1.0f) );
	m_pSplineCtrl->RemoveAllSplines();

	if (varInfo.type == ITimeOfDay::TYPE_COLOR)
	{
		COLORREF	afColorArray[4];
		afColorArray[0]=RGB(255,0,0);
		afColorArray[1]=RGB(0,255,0);
		afColorArray[2]=RGB(0,0,255);
		afColorArray[3]=RGB(255,0,255); //Pink... so you know it's wrong if you see it.
		m_pSplineCtrl->AddSpline( varInfo.pInterpolator,0,afColorArray);
		m_pSplineCtrl->SetValueRange( Range(0,1) );

		m_pColorGradientCtrl->SetSpline( varInfo.pInterpolator,TRUE );
		m_pColorGradientCtrl->EnableWindow(TRUE);
			m_pColorGradientCtrl->Invalidate(FALSE);

	}
	else
	{	
		m_pColorGradientCtrl->EnableWindow(FALSE);
		m_pColorGradientCtrl->Invalidate();

		m_pSplineCtrl->SetValueRange( Range(varInfo.fValue[1],varInfo.fValue[2]) );
		m_pSplineCtrl->AddSpline( varInfo.pInterpolator,0,RGB(0,255,0) );
	}
	m_pSplineCtrl->SetSplineSet( 0 );
	}
	else
	{
		m_pSplineCtrl->RemoveAllSplines();
		m_pColorGradientCtrl->EnableWindow(FALSE);
	}
	m_pSplineCtrl->Invalidate(FALSE);
}


//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnHold()
{
	XmlNodeRef node = XmlHelpers::CreateXmlNode("TimeOfDay");
	GetIEditor()->Get3DEngine()->GetTimeOfDay()->Serialize( node,false );
	node->saveToFile( "%USER%/Sandbox/TimeOfDayHold.xml" );
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnFetch()
{
	XmlNodeRef node = GetISystem()->LoadXmlFromFile( "%USER%/Sandbox/TimeOfDayHold.xml" );
	if (node)
	{
		GetIEditor()->Get3DEngine()->GetTimeOfDay()->Serialize( node,true );
		UpdateValues();
		SetTime( GetTime(),true );
	}
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnUndo()
{
	GetIEditor()->Undo();
}

//////////////////////////////////////////////////////////////////////////
void CTimeOfDayDialog::OnRedo()
{
	GetIEditor()->Redo();
}

//////////////////////////////////////////////////////////////////////////
BOOL CTimeOfDayDialog::PreTranslateMessage( MSG* pMsg )
{
	if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST)
	{
		// skip MainFrame accelerators
		::TranslateMessage(pMsg);
		::DispatchMessage(pMsg);
		return TRUE;
	}

	if ( pMsg->message == WM_INPUT)
	{
		static C3DConnexionDriver* p3DConnexionDriver = 0;

		if (!p3DConnexionDriver)
		{
			p3DConnexionDriver = (C3DConnexionDriver*)GetIEditor()->GetPluginManager()->GetPluginByGUID( "{AD109901-9128-4ffd-8E67-137CB2B1C41B}" );
		}
		if (p3DConnexionDriver)
		{
			S3DConnexionMessage msg;
			if (p3DConnexionDriver->GetInputMessageData( pMsg->lParam,msg ))
			{
				if (msg.bGotRotation)
				{
					float fDelta2 = msg.vRotate[2] * 0.1f;
					float fTime = GetTime();
					SetTime( fTime+fDelta2 );
				}
			}
		}
	}

	bool bRetVal = __super::PreTranslateMessage(pMsg);

	gSettings.bForceSkyUpdate = m_forceUpdateCheck.GetCheck() == BST_CHECKED;

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////
IVariable * CTimeOfDayDialog::FindVariable(const char *name) const
{
		IVariable *pVar = m_pVars->FindVariable(name);
		if (!pVar)
		{
			pVar = m_pHDRPane->m_pVars->FindVariable(name);
		}

		return pVar;
}
